use std::error::Error;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use Esp32_Tcp_Server::config::Config;
use sqlx::mysql::MySqlPoolOptions;
use sqlx::{MySql, Pool};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 获取config
    let config = Config::read_to_config("config.toml".to_string());

    // 连接数据库
    let database_url = config.get_database_url();
    let pool = MySqlPoolOptions::new().max_connections(20).connect(&database_url).await.expect("数据库无法连接,请检查后重新启动程序!");
    println!("数据库连接池创建成功！");
    let pool = Arc::new(pool);

    // tcp服务器启动
    let listener = TcpListener::bind(config.server.addr.clone()).await.expect("Failed to listen server address");
    println!("Sever地址：{}, 服务器启动开始监听...",config.server.addr);

    loop {
        let (socket, clientAddr) = listener.accept().await?;
        let pool = Arc::clone(&pool);
        tokio::spawn(async move {
            match handle_client(socket, clientAddr.clone(), pool).await {
                Ok(_) => {}
                Err(e) => {
                    let now = Esp32_Tcp_Server::tools::get_time_now();
                    println!("{}: 发生错误, clientaddr:{}, Err: {:?}",now,clientAddr, e);
                }
            };
        });
    }
}


// tcp处理程序
async fn handle_client(mut socket: tokio::net::TcpStream,clientAddr:SocketAddr,pool:Arc<Pool<MySql>>) -> Result<(), Box<dyn std::error::Error>> {
    let mut buffer = [0; 1024];

    // Read message from client
    let len = socket.read(&mut buffer).await?;
    if len == 0 {
        return Ok(());
    }

    let message = std::str::from_utf8(&buffer[..len])?;
    let now = Esp32_Tcp_Server::tools::get_time_now();

    println!("Received from client: {}, datatime: {}, message: {}", clientAddr,now,message);

    // 回复信息
    let response = "hdx";
    socket.write_all(response.as_bytes()).await?;

    // 插入数据库
    sqlx::query(
        r#"
        INSERT INTO log (addr, message)
        VALUES (?, ?)
        "#,
    ).bind(clientAddr.to_string()).bind(message.to_string()).execute(&*pool).await?;

    Ok(())
}
